/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file dcPackerInterface.I
 * @author drose
 * @date 2004-06-18
 */

/**
 * Returns the name of this field, or empty string if the field is unnamed.
 */
INLINE const std::string &DCPackerInterface::
get_name() const {
  return _name;
}

/**
 * Returns true if the other interface is bitwise the same as this one--that
 * is, a uint32 only matches a uint32, etc.  Names of components, and range
 * limits, are not compared.
 */
INLINE bool DCPackerInterface::
check_match(const DCPackerInterface *other) const {
  return do_check_match(other);
}

/**
 * Returns true if this field type always packs to the same number of bytes,
 * false if it is variable.
 */
INLINE bool DCPackerInterface::
has_fixed_byte_size() const {
  return _has_fixed_byte_size;
}

/**
 * If has_fixed_byte_size() returns true, this returns the number of bytes
 * this field type will use.
 */
INLINE size_t DCPackerInterface::
get_fixed_byte_size() const {
  return _fixed_byte_size;
}

/**
 * Returns true if this field type always has the same structure regardless of
 * the data in the stream, or false if its structure may vary.  This is
 * almost, but not quite, the same thing as has_fixed_byte_size.  The
 * difference is that a DCSwitch may have multiple cases all with the same
 * byte size, but they will still (presumably) have different structures, in
 * the sense that the actual list of fields varies according to the live data.
 */
INLINE bool DCPackerInterface::
has_fixed_structure() const {
  return _has_fixed_structure;
}

/**
 * Returns true if this field, or any sub-field of this field, has a limit
 * imposed in the DC file on its legal values.  If this is false, then
 * unpack_validate() is trivial.
 */
INLINE bool DCPackerInterface::
has_range_limits() const {
  return _has_range_limits;
}

/**
 * Returns the number of bytes that should be written into the stream on a
 * push() to record the number of bytes in the record up until the next pop().
 * This is only meaningful if _has_nested_fields is true.
 */
INLINE size_t DCPackerInterface::
get_num_length_bytes() const {
  return _num_length_bytes;
}


/**
 * Returns true if this field type has any nested fields (and thus expects a
 * push() .. pop() interface to the DCPacker), or false otherwise.  If this
 * returns true, get_num_nested_fields() may be called to determine how many
 * nested fields are expected.
 */
INLINE bool DCPackerInterface::
has_nested_fields() const {
  return _has_nested_fields;
}

/**
 * Returns the number of nested fields required by this field type.  These may
 * be array elements or structure elements.  The return value may be -1 to
 * indicate the number of nested fields is variable.
 */
INLINE int DCPackerInterface::
get_num_nested_fields() const {
  return _num_nested_fields;
}

/**
 * Returns the type of value expected by this field.
 */
INLINE DCPackType DCPackerInterface::
get_pack_type() const {
  return _pack_type;
}

/**
 *
 */
INLINE void DCPackerInterface::
do_pack_int8(char *buffer, int value) {
  buffer[0] = (char)(value & 0xff);
}

/**
 *
 */
INLINE void DCPackerInterface::
do_pack_int16(char *buffer, int value) {
  buffer[0] = (char)(value & 0xff);
  buffer[1] = (char)((value >> 8) & 0xff);
}

/**
 *
 */
INLINE void DCPackerInterface::
do_pack_int32(char *buffer, int value) {
  buffer[0] = (char)(value & 0xff);
  buffer[1] = (char)((value >> 8) & 0xff);
  buffer[2] = (char)((value >> 16) & 0xff);
  buffer[3] = (char)((value >> 24) & 0xff);
}

/**
 *
 */
INLINE void DCPackerInterface::
do_pack_int64(char *buffer, int64_t value) {
  buffer[0] = (char)(value & 0xff);
  buffer[1] = (char)((value >> 8) & 0xff);
  buffer[2] = (char)((value >> 16) & 0xff);
  buffer[3] = (char)((value >> 24) & 0xff);
  buffer[4] = (char)((value >> 32) & 0xff);
  buffer[5] = (char)((value >> 40) & 0xff);
  buffer[6] = (char)((value >> 48) & 0xff);
  buffer[7] = (char)((value >> 56) & 0xff);
}

/**
 *
 */
INLINE void DCPackerInterface::
do_pack_uint8(char *buffer, unsigned int value) {
  buffer[0] = (char)(value & 0xff);
}

/**
 *
 */
INLINE void DCPackerInterface::
do_pack_uint16(char *buffer, unsigned int value) {
  buffer[0] = (char)(value & 0xff);
  buffer[1] = (char)((value >> 8) & 0xff);
}

/**
 *
 */
INLINE void DCPackerInterface::
do_pack_uint32(char *buffer, unsigned int value) {
  buffer[0] = (char)(value & 0xff);
  buffer[1] = (char)((value >> 8) & 0xff);
  buffer[2] = (char)((value >> 16) & 0xff);
  buffer[3] = (char)((value >> 24) & 0xff);
}

/**
 *
 */
INLINE void DCPackerInterface::
do_pack_uint64(char *buffer, uint64_t value) {
  buffer[0] = (char)(value & 0xff);
  buffer[1] = (char)((value >> 8) & 0xff);
  buffer[2] = (char)((value >> 16) & 0xff);
  buffer[3] = (char)((value >> 24) & 0xff);
  buffer[4] = (char)((value >> 32) & 0xff);
  buffer[5] = (char)((value >> 40) & 0xff);
  buffer[6] = (char)((value >> 48) & 0xff);
  buffer[7] = (char)((value >> 56) & 0xff);
}

/**
 *
 */
INLINE void DCPackerInterface::
do_pack_float64(char *buffer, double value) {
#ifdef WORDS_BIGENDIAN
  // Reverse the byte ordering for big-endian machines.
  char *p = (char *)&value;
  for (size_t i = 0; i < 8; i++) {
    buffer[i] = p[7 - i];
  }
#else
  memcpy(buffer, &value, 8);
#endif
}


/**
 *
 */
INLINE int DCPackerInterface::
do_unpack_int8(const char *buffer) {
  return (int)(signed char)buffer[0];
}

/**
 *
 */
INLINE int DCPackerInterface::
do_unpack_int16(const char *buffer) {
  return (int)((unsigned int)(unsigned char)buffer[0] |
               ((int)(signed char)buffer[1] << 8));
}

/**
 *
 */
INLINE int DCPackerInterface::
do_unpack_int32(const char *buffer) {
  return (int)((unsigned int)(unsigned char)buffer[0] |
               ((unsigned int)(unsigned char)buffer[1] << 8) |
               ((unsigned int)(unsigned char)buffer[2] << 16) |
               ((int)(signed char)buffer[3] << 24));
}

/**
 *
 */
INLINE int64_t DCPackerInterface::
do_unpack_int64(const char *buffer) {
  return (int64_t)((uint64_t)(unsigned char)buffer[0] |
                    ((uint64_t)(unsigned char)buffer[1] << 8) |
                    ((uint64_t)(unsigned char)buffer[2] << 16) |
                    ((uint64_t)(unsigned char)buffer[3] << 24) |
                    ((uint64_t)(unsigned char)buffer[4] << 32) |
                    ((uint64_t)(unsigned char)buffer[5] << 40) |
                    ((uint64_t)(unsigned char)buffer[6] << 48) |
                    ((int64_t)(signed char)buffer[7] << 56));
}

/**
 *
 */
INLINE unsigned int DCPackerInterface::
do_unpack_uint8(const char *buffer) {
  return (unsigned int)(unsigned char)buffer[0];
}

/**
 *
 */
INLINE unsigned int DCPackerInterface::
do_unpack_uint16(const char *buffer) {
  return ((unsigned int)(unsigned char)buffer[0] |
          ((unsigned int)(unsigned char)buffer[1] << 8));
}

/**
 *
 */
INLINE unsigned int DCPackerInterface::
do_unpack_uint32(const char *buffer) {
  return ((unsigned int)(unsigned char)buffer[0] |
          ((unsigned int)(unsigned char)buffer[1] << 8) |
          ((unsigned int)(unsigned char)buffer[2] << 16) |
          ((unsigned int)(unsigned char)buffer[3] << 24));
}

/**
 *
 */
INLINE uint64_t DCPackerInterface::
do_unpack_uint64(const char *buffer) {
  return ((uint64_t)(unsigned char)buffer[0] |
          ((uint64_t)(unsigned char)buffer[1] << 8) |
          ((uint64_t)(unsigned char)buffer[2] << 16) |
          ((uint64_t)(unsigned char)buffer[3] << 24) |
          ((uint64_t)(unsigned char)buffer[4] << 32) |
          ((uint64_t)(unsigned char)buffer[5] << 40) |
          ((uint64_t)(unsigned char)buffer[6] << 48) |
          ((uint64_t)(unsigned char)buffer[7] << 56));
}

/**
 *
 */
INLINE double DCPackerInterface::
do_unpack_float64(const char *buffer) {
#ifdef WORDS_BIGENDIAN
  char reverse[8];

  // Reverse the byte ordering for big-endian machines.
  for (size_t i = 0; i < 8; i++) {
    reverse[i] = buffer[7 - i];
  }
  return *(double *)reverse;
#else
  return *(double *)buffer;
#endif  // WORDS_BIGENDIAN
}

/**
 * Confirms that the signed value fits within num_bits bits.  Sets range_error
 * true if it does not.
 */
INLINE void DCPackerInterface::
validate_int_limits(int value, int num_bits, bool &range_error) {
  // What we're really checking is that all of the bits above the lower
  // (num_bits - 1) bits are the same--either all 1 or all 0.

  // First, turn on the lower (num_bits - 1).
  int mask = ((int)1 << (num_bits - 1)) - 1;
  value |= mask;

  // The result should be either mask (all high bits are 0) or -1 (all high
  // bits are 1).  If it is anything else we have a range error.
  if (value != mask && value != -1) {
    range_error = true;
  }
}

/**
 * Confirms that the signed value fits within num_bits bits.  Sets range_error
 * true if it does not.
 */
INLINE void DCPackerInterface::
validate_int64_limits(int64_t value, int num_bits, bool &range_error) {
  int64_t mask = ((int64_t)1 << (num_bits - 1)) - 1;
  value |= mask;

  if (value != mask && value != -1) {
    range_error = true;
  }
}

/**
 * Confirms that the unsigned value fits within num_bits bits.  Sets
 * range_error true if it does not.
 */
INLINE void DCPackerInterface::
validate_uint_limits(unsigned int value, int num_bits, bool &range_error) {
  // Here we're really checking that all of the bits above the lower num_bits
  // bits are all 0.

  unsigned int mask = ((unsigned int)1 << num_bits) - 1;
  value &= ~mask;

  if (value != 0) {
    range_error = true;
  }
}

/**
 * Confirms that the unsigned value fits within num_bits bits.  Sets
 * range_error true if it does not.
 */
INLINE void DCPackerInterface::
validate_uint64_limits(uint64_t value, int num_bits, bool &range_error) {
  uint64_t mask = ((uint64_t)1 << num_bits) - 1;
  value &= ~mask;

  if (value != 0) {
    range_error = true;
  }
}
